(* LES TYPES SOMME *)

(* ACCOUPLEMENT *)

(* TYPE *)

type element =
  | Water
  | Fire
  | Wind
  | Earth
  | None
  | Derived of string*element*element ;;

(* COMBINE *)

let rec combine elem list_elem = match list_elem with
  | [] -> []
  | h::t -> (elem, h)::(combine elem t) ;;
combine Water [Water; Fire; Earth] ;;

(* COMBINE_NO_DOUBLE *)

let rec combine_no_double elem list_elem = match list_elem with
  | [] -> []
  | h::t -> if h=elem then
      combine_no_double elem t
    else
      (elem, h)::(combine_no_double elem t) ;;
combine_no_double Water [Water; Fire; Earth] ;;

(* ALL_COMBINAISON *)

let rec all_combinaisons l = match l with
  | [] -> []
  | h::t -> (combine h l)@all_combinaisons t ;;
all_combinaisons [Water; Fire; Earth] ;;

(* BONUS 1 *)

let rec all_combinaisons_2 l = match l with
  | [] -> []
  | h::t -> (combine h::t)all_combinaison_2 t ;;
all_combinaisons_2 [Water; Fire; Earth] ;;

(* AFFICHAGE *)

(* GET_STRING *)

let get_string = function
  | Water -> "Water"
  | Fire -> "Fire"
  | Earth -> "Earth"
  | Wind -> "Fire"
  | None -> "None"
  | Derived (str, e1, e2) ->  str ;;

(* GET_FORMULA *)

let get_formula = function
  | Water -> "Water"
  | Fire -> "Fire"
  | Earth -> "Earth"
  | Wind -> "Wind"
  | None -> "None"
  | Derived (str, e1, e2) -> str^" = "^get_string e1^" + "^get_string e2 ;;
get_formula (Derived ("Alcohol", Fire, Water) );;
   
(* PRINT_ELT *)

let print_elt elem = print_string (get_formula elem) ;;
print_elt (Derived ("Alcohol", Fire, Water)) ;;

(* PRINT_LIST *)

let rec print_list  = function
  | [] -> print_string ""
  | h::t -> print_elt h;
    print_string "\n";
    print_list t ;;
print_list [Water; Fire; Earth; Derived ("Alcohol", Fire, Water)] ;;

(* BRUTE FORCE *)

let set = [(("Fire", "Water") , "Alcohol") ;
           (("Water", "Fire") , "Whiskey") ;
           (("Earth", "Fire"), "Lava") ;
           (("Lava","Wind") , "Stone") ;
           (("Earth","Seed") ,"Tree") ;
           (("Life","Stone") , "Egg") ;
           (("Life","Swamp") , "Beast") ;
           (("Fire","Stone") , "Metal") ;
           (("Earth","Water") , "Swamp") ;
           (("Energy","Swamp") , "Life") ;
           (("Earth","Life") , "Seed") ;
           (("Seed","Earth") , "Flower") ;
           (("Fire","Fire") , "Sun") ;
           (("Flower","Sun") , "Junior") ;
           (("Water","Water") , "Sea") ;
           (("Wind","Wind") , "Tornado") ;
           (("Beast","Life") , "Man") ;
           (("Man","Egg"), "Baby") ;
           (("Alcohol","Whiskey"), "Elixir");
           (("Fire","Man") , "Krisboul") ;
           (("Krisboul","Junior") , "SYNTAX_ERROR") ;
           (("Fire", "Wind") , "Energy");
           (("Energy","Tornado") , "Thunder") ;
           (("Krisboul","Thunder") , "Zeus")];;

(* FORMULA *)

let rec formula str = match str with
  | "Fire" -> Fire
  | "Water" -> Water
  | "Earth" -> Earth
  | "Wind" -> Wind
  | "None" -> None
  | str -> let  rec rformula str l = match l with
    | [] -> invalid_arg "n'existe pas"
    | ((e1, e2), e)::t when str = e -> Derived (e, formula e1, formula e2)
    | h::t -> rformula str t
           in
    rformula str set ;;
formula "Energy" ;;
formula "nicolas" ;;
formula "Krisboul" ;;

(* MERGE *)

let merge (element1, element2) =
  let rec cherche (a, b) list = match list with
    | ((e1, e2), element)::t when (get_string a, get_string b) = (e1, e2) -> 
      Derived (element, element1, element2)
    | _::t -> cherche (a, b) t
    | _ -> None
  in
    cherche (element1, element2) set ;;

merge (Fire, Water) ;;
